package com.lingjtx.common.security.interceptor;

import com.lingjtx.common.core.ResponseResult;
import com.lingjtx.common.core.annotation.IgnoreLogin;
import com.lingjtx.common.core.constant.Const;
import com.lingjtx.common.core.token.UserVo;
import com.lingjtx.common.redis.service.IRedisService;
import com.lingjtx.common.security.common.Utils;
import com.lingjtx.common.security.strategy.TokenContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;

import java.util.Objects;

/**
 * 拦截器Handler
 * <p>
 * 需要登录的接口做标记注解，在拦截器中，从请求头里获取这一类接口的token。根据token 解密出payload 中的exp(过期时间) 的值
 * 1) exp 在有效期内，缓存中无token，（说明缓存记录被删除，用户在另一个设备登录）
 * 2) exp 时间过期，直接返回token过期
 * <p>
 * <p>
 * token设置为单账号在线，账号新登录，之前账号即下线，token缓存删除
 * 后期，做电脑和手机app共同在线的话，token的payload设置"aud":"用户登录的平台（PC,Android,iOS,小程序）"
 * 1、 先判断缓存中有没有key为uuid的access_token缓存，有的话删除
 * 2、 设置key为access_token的缓存用户信息，缓存refresh_token，并设置access_token有效期（有效期为refresh_token的有效期）
 * </p>
 *
 * @author John Liu
 */
@Component
public class HttpInterceptor implements HandlerInterceptor {

    private final IRedisService redisService;
    private final TokenContext tokenContext;

    public HttpInterceptor(IRedisService redisService, TokenContext tokenContext) {
        this.redisService = redisService;
        this.tokenContext = tokenContext;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof ResourceHttpRequestHandler) {
            return true;
        }
        if (handler instanceof HandlerMethod method) {
            IgnoreLogin ignoreLogin = method.getMethodAnnotation(IgnoreLogin.class);
            if (Objects.nonNull(ignoreLogin) && ignoreLogin.required()) {// 忽略api接口的token认证
                return true;
            }
            String token = request.getHeader(Const.AuthTokenMap.AUTHORIZATION);

            if (!tokenContext.process(redisService, token)) {
                Utils.printResponseResult(response, tokenContext.getErrorResponseResult());
                return false;
            }
            UserVo user = (UserVo) redisService.get(token);
            // 传递user给下游
            UserContext.setUser(user);
            return true;
        }
        Utils.printResponseResult(response, ResponseResult.createByError("未知错误"));
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 清除 ThreadLocal，防止内存溢出
        UserContext.clear();
    }
}